#pragma once
#include "../../snappy/snappy.h"

namespace umtl
{
	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	connection< protocol >::connection( read_handler_t & read_handler, close_handler_t & close_handler )
		: socket_(io_service_)
		, bytes_reserved_(0)
		, bytes_transferred_(0)
		, read_handler_( socket_.strand_, read_handler )
		, close_handler_( close_handler )
	{
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	connection< protocol > * connection< protocol >::create( read_handler_t & read_handler, close_handler_t & close_handler )
	{
		return new connection(read_handler, close_handler);
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	void connection< protocol >::listen( unsigned short int port )
	{
		socket_.listen( port );
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	void connection< protocol >::connect( std::string const  & host, std::string const & port )
	{
		socket_.connect( host, port );
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	void connection< protocol >::read()
	{
		auto buffer = boost::asio::buffer( &read_buffer_.front(), read_buffer_.size() );

		socket_.read( 
			buffer,
			//--------------
			// call back
			[this]( boost::system::error_code const & error, size_t bytes_transferred )
			{
				if( !error || error == boost::asio::error::message_size )
				{
					read_packets_.insert( read_packets_.end(), read_buffer_.begin(), read_buffer_.begin() + bytes_transferred );

					parse_packet();

					read();
				}
				else if(error != boost::asio::error::operation_aborted)
				{
					socket_.socket_->close();
					close_handler_( this );
					//throw boost::system::system_error(error);
				}
			}
			//--------------
		);
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	void connection< protocol >::send( char const * data, size_t len/*, std::tr1::function< void(char const *) > release_data*/ )
	{
		if( data && len )
		{
			auto buffer = boost::asio::buffer(data, len);

			socket_.send(
				buffer,
				//--------------
				// call back
				[this, data, len]( boost::system::error_code const & error, size_t bytes_transferred )
				{
					if( error && error != boost::asio::error::eof )
					{
						socket_.socket_->close();

						//release_data(data);

						close_handler_( this );
					}
					else
					{
						bytes_transferred_ += bytes_transferred;
						//if( len <= bytes_transferred )
						//	release_data(data);
					}
				}
				//--------------
			);
		}
	}

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	void connection< protocol >::run() 
	{
		io_service_.run();
	} 

	//////////////////////////////////////////////////////////////////////////
	//

	template< typename protocol >
	void connection< protocol >::parse_packet()
	{
		if( !read_packets_.empty() )
		{
			if( bytes_reserved_ )
			{
				if( bytes_reserved_ <= read_packets_.size() )
				{
					/*
					size_t compSize = bytes_reserved_;

					char const * compData = &read_packets_.front();

					size_t unCompSize = 0;

					if( snappy::GetUncompressedLength( compData, compSize, &unCompSize ) )
					{
						char * packet = (char*)memory_manager::get().alloc( unCompSize );

						//if( snappy::RawUncompress( compData, compSize, packet ) )
						snappy::RawUncompress( compData, compSize, packet );
						{
							read_handler_(this, packet, unCompSize);
						}

						memory_manager::get().free( packet );
					}
					*/

					char const * packet = &read_packets_.front();

					read_handler_( this, packet, bytes_reserved_ );

					read_packets_.erase( &read_packets_[0], &read_packets_[bytes_reserved_] );

					bytes_reserved_ = 0;
				}
			}
			
			if( bytes_reserved_ == 0 && read_packets_.size() >= sizeof(unsigned int) )
			{
				bytes_reserved_ = *(unsigned int*)&read_packets_.front();

				if( bytes_reserved_ == 0 )
				{
					std::string udp_accept_msg = "udp accept";
					read_handler_(this, udp_accept_msg.c_str(), udp_accept_msg.length()+1);
				}
				else
				{
					if( bytes_reserved_ > sizeof( unsigned int ) )
						bytes_reserved_ -= sizeof(unsigned int);
					else
						bytes_reserved_ = 0;
				}

				read_packets_.erase( &read_packets_[0], &read_packets_[sizeof(unsigned int)] );

				parse_packet();
			}
		}
	}

	//////////////////////////////////////////////////////////////////////////
}